package com.rou.im.framework.boot.starter.builder;

import com.rou.im.framework.boot.starter.Builder;
import com.rou.im.framework.boot.starter.Configurer;
import com.rou.im.framework.boot.starter.postprocessor.ObjectPostProcessor;
import com.rou.im.framework.core.processorstack.ProcessorStack;
import org.springframework.util.Assert;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * <p></p>
 *
 * @author GaoShuangWen
 * @since 2020-05-26 14:37
 */
public abstract class AbstractProcessorStackConfigureBuilder<B extends Builder<ProcessorStack>> extends AbstractBuilder<ProcessorStack> {


    Map<Class<? extends Configurer<B>>, Configurer<B>> configurers = new ConcurrentHashMap<>();

    private ObjectPostProcessor<Object> objectPostProcessor;


    protected AbstractProcessorStackConfigureBuilder(
            ObjectPostProcessor<Object> objectPostProcessor) {
        Assert.notNull(objectPostProcessor, "objectPostProcessor cannot be null");
        this.objectPostProcessor = objectPostProcessor;
    }

    public ObjectPostProcessor<Object> getObjectPostProcessor() {
        return objectPostProcessor;
    }

    public <C extends Configurer<B>> C apply(C configurer) throws Exception {
        add(configurer);
        return configurer;
    }

    protected <C extends Configurer<B>> void add(C configurer) throws Exception {
        Assert.notNull(configurer, "configurer cannot be null");

        Class<? extends Configurer<B>> clazz = (Class<? extends Configurer<B>>) configurer.getClass();

        configurers.put(clazz, configurer);
    }

    public <C extends Configurer<B>> C getConfigurer(Class<C> clazz) {

        return (C) configurers.get(clazz);
    }

    public <C extends Configurer<B>> C removeConfigurer(Class<C> clazz) {
        Configurer<B> remove = configurers.remove(clazz);
        return (C) remove;
    }

    @SuppressWarnings("unchecked")
    protected void init() throws Exception {
        for (Map.Entry<Class<? extends Configurer<B>>, Configurer<B>> entry : configurers.entrySet()) {
            entry.getValue().init((B) this);
        }

    }

    @SuppressWarnings("unchecked")
    protected void configure() throws Exception {
        for (Map.Entry<Class<? extends Configurer<B>>, Configurer<B>> entry : configurers.entrySet()) {
            entry.getValue().configure((B) this);
        }
    }

    protected <P> P postProcess(P object) {
        return this.objectPostProcessor.postProcess(object);
    }

    @Override
    protected final ProcessorStack doBuild() throws Exception {
        beforeInit();

        init();

        beforeConfigure();

        configure();

        return performBuild();

    }

    protected void beforeInit() throws Exception {
    }

    protected void beforeConfigure() throws Exception {
    }
    protected abstract ProcessorStack performBuild();
}
